import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, CancelTokenSource } from 'axios';
import { debounce } from 'lodash-es';
import { ElMessage } from 'element-plus';
import { getItem } from './componen'; // 假设是本地存储封装
import router from '../router';

// ==================== 类型扩展 ====================
// 扩展 axios 请求配置，支持自定义字段
declare module 'axios' {
  interface AxiosRequestConfig {
    isRepeat?: boolean; // 是否允许重复请求（默认不允许）
    showError?: boolean; // 是否显示错误提示（默认显示）
  }
}

// ==================== 常量定义 ====================
const TIMEOUT = 50_000; // 请求超时时间
const LOGIN_TIMEOUT_CODE = 100; // 登录过期业务码
const SUCCESS_CODE = 0; // 接口成功状态码
const DEBOUNCE_WAIT = 500; // 防抖等待时间

// ==================== 工具函数 ====================

/**
 * 对对象的所有键进行递归排序，用于生成稳定的请求 key
 */
function sortObjectKeys(obj: Record<string, any>): Record<string, any> {
  if (typeof obj !== 'object' || obj === null) return obj;

  return Object.keys(obj)
    .sort()
    .reduce(
      (acc, key) => {
        acc[key] = sortObjectKeys(obj[key]);
        return acc;
      },
      {} as Record<string, any>,
    );
}

/**
 * 序列化 FormData 数据，保证顺序一致，用于生成唯一请求 key
 */
function serializeFormData(formData: FormData): string {
  const entries = Array.from(formData.entries());
  entries.sort(([a], [b]) => a.localeCompare(b)); // 按 key 排序
  return JSON.stringify(entries);
}

/**
 * 根据请求配置生成唯一 key，用于防重请求判断
 */
function generateRequestKey(config: AxiosRequestConfig): string {
  const { method = 'get', url, params, data } = config;

  const paramsStr = params ? JSON.stringify(sortObjectKeys(params)) : '';
  let dataStr = '';

  if (data) {
    if (data instanceof FormData) {
      dataStr = serializeFormData(data);
    } else if (typeof data === 'object') {
      dataStr = JSON.stringify(sortObjectKeys(data));
    } else {
      dataStr = String(data);
    }
  }

  return `${method}-${url}-${paramsStr}-${dataStr}`;
}

// ==================== 实例创建 ====================
const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: TIMEOUT,
});

// ==================== 请求拦截管理 ====================
const pendingRequests = new Map<string, CancelTokenSource>(); // 存储正在请求的 token
let isRedirecting = false; // 防止重复跳转登录页

/**
 * 取消指定 key 的挂起请求
 */
function cancelPendingRequest(key: string): void {
  if (pendingRequests.has(key)) {
    const source = pendingRequests.get(key)!;
    source.cancel('请求已取消，因为有新的相同请求发出');
    pendingRequests.delete(key);
  }
}

/**
 * 添加请求到待处理队列中，并取消之前的重复请求
 */
function addPendingRequest(config: AxiosRequestConfig): void {
  const key = generateRequestKey(config);
  cancelPendingRequest(key); // 如果存在旧请求则先取消

  const source = axios.CancelToken.source();
  pendingRequests.set(key, source);
  config.cancelToken = source.token;
}

/**
 * 请求完成后从队列中移除
 */
function removePendingRequest(config: AxiosRequestConfig): void {
  const key = generateRequestKey(config);
  pendingRequests.delete(key);
}

// ==================== 认证相关 ====================
/**
 * 获取本地存储的 token
 */
function getAuthTokens() {
  return {
    apiToken: getItem('apiToken'),
    loginToken: getItem('loginToken'),
    authenticationToken: getItem('authenticationToken'),
  };
}

/**
 * 检查用户是否已登录
 */
function checkLoginStatus(): boolean {
  const { apiToken } = getAuthTokens();

  if (!apiToken && !isRedirecting) {
    isRedirecting = true;
    router.push('/login').then(() => {
      isRedirecting = false;
    });
    return false;
  }

  return !!apiToken;
}

// ==================== 请求拦截器 ====================
service.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    // 设置默认值
    config.showError = config.showError ?? true;

    // // 检查登录状态
    // if (!checkLoginStatus()) {
    //   const source = axios.CancelToken.source();
    //   config.cancelToken = source.token;
    //   source.cancel('未登录，请求已被取消');
    //   return config;
    // }

    // 非重复请求添加防重逻辑
    if (!config.isRepeat) {
      addPendingRequest(config);
    }

    // 自动注入 token 到 header
    const { apiToken, loginToken, authenticationToken } = getAuthTokens();
    if (apiToken) config.headers.apiToken = apiToken;
    if (loginToken) config.headers.loginToken = loginToken;
    if (authenticationToken) config.headers.authorization = `Bearer ${authenticationToken}`;

    return config;
  },
  (error) => {
    ElMessage.error(error.message || '请求发送失败');
    return Promise.reject(error);
  },
);

// ==================== 响应拦截器 ====================
service.interceptors.response.use(
  (response: AxiosResponse) => {
    removePendingRequest(response.config);
    const res = response.data;

    // 登录超时
    if (res.state === LOGIN_TIMEOUT_CODE) {
      ElMessage.error('登录已超时，请重新登录');
      router.push('/login').then(() => {
        isRedirecting = false;
      });
      return Promise.reject(res);
    }

    // 业务错误处理
    if (res.state !== SUCCESS_CODE && res.errorMessage) {
      if (response.config?.showError !== false) {
        ElMessage.error(res.errorMessage);
      }
      return Promise.reject(res);
    }

    // 返回数据格式兼容处理
    return res.data ?? res;
  },
  (error) => {
    if (error.config) removePendingRequest(error.config);

    if (axios.isCancel(error)) {
      console.log('请求已取消:', error.message);
      return Promise.reject(error);
    }

    // HTTP 错误码处理
    const status = error.response?.status;
    const defaultMessage = error.response?.data?.errorMessage || error.message || '请求失败，请稍后重试';

    switch (status) {
      case 401:
        ElMessage.error('登录状态已失效，请重新登录');
        if (!isRedirecting) {
          isRedirecting = true;
          router.push('/login').then(() => {
            isRedirecting = false;
          });
        }
        break;
      case 403:
        ElMessage.error('没有权限访问该资源');
        break;
      case 404:
        ElMessage.error('请求的资源不存在');
        break;
      case 500:
        ElMessage.error('服务器内部错误，请稍后重试');
        break;
      default:
        ElMessage.error(defaultMessage);
    }

    return Promise.reject(error);
  },
);

// ==================== 防抖包装器 ====================
/**
 * 防抖装饰器，用于包装高频请求方法
 */
function debounceRequest<T extends (...args: any[]) => Promise<any>>(func: T, wait: number = DEBOUNCE_WAIT): T {
  return debounce(func, wait) as unknown as T;
}

// ==================== 导出 ====================
export default service;
export { debounceRequest };
